using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using NLog;
using System.Diagnostics;
using System.Data;
using System.IO;

namespace MyClever.Lib
{    
    public class MissionPluginProxy : MissionPluginNode
    {       
        private static Logger logger = LogManager.GetCurrentClassLogger();

        protected static int numPluginProxies;

        private PluginBuilder pluginBuilder;

        /// <summary>
        /// Contains the input-settings and output-settings.
        /// For example: csv-file-path to read, rss-url to ask ...
        /// This settings will preset in the mission.xml-file. 
        /// * Settings: (Mission/Source/Setting or Mission/Action/Setting)
        /// * Output: (Mission/Source/Output or Mission/Action/Output)
        /// </summary>
        private MissionPluginPackageSet packages;

        /// <summary>
        /// Gets an unique plugin ID to compare with another plugins.
        /// </summary>
        /// <value>A simple unique plugin ID.</value>
        public override int ID
        {
            get { return this.id; }
        }

        /// <summary>
        /// Gets the file informations of the loaded plugin.
        /// </summary>
        /// <value>The file-informations of the loaded plugin.</value>
        public PluginPath Path
        {
            get
            {
                return this.pluginBuilder.Path;
            }
        }

        /// <summary>
        /// Gets the name of the loaded plugin.
        /// </summary>
        /// <value>The name of the loaded plugin.</value>
        public string ClassName
        {
            get
            {
                return this.pluginBuilder.ClassName;
            }
        }

        public ProgrammingLanguage ProgrammingLanguage
        {
            get
            {
                return this.pluginBuilder.ProgrammingLanguage;
            }
        }

        public override MissionPluginLoopNode Parent
        {
            get { return this.parentNode; }
            set { this.parentNode = value;  }
        }

        public object GetProperty(PluginProperty type)
        {
            return this.pluginBuilder.Properties[type];
        }

        /// <summary>
        /// Gets the logging package.
        /// </summary>
        /// <value>The logging package.</value>
        private MissionPluginPackage LoggingPackage
        {
            get
            {
                List<MissionPluginPackage> packages = this.GetPackages(Direction.Logging);
                Debug.Assert(packages.Count == 1);
                return packages[0];
            }
        }

        public MissionPluginProxy(AvailablePlugin availablePlugin)
            : this((++MissionPluginProxy.numPluginProxies), availablePlugin.Path, availablePlugin.FullName, availablePlugin.Language)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MissionPluginData"/> class.
        /// </summary>
        /// <param name="id">An unique plugin-id.</param>
        public MissionPluginProxy(int pluginID, PluginPath pluginPath, string pluginFullName, ProgrammingLanguage pluginLanguage)
        {
            this.id = pluginID;
            MissionPluginProxy.numPluginProxies = pluginID; // TODO: this is only a hack!

            // load the plugin by the expected pluginAttributes
            this.pluginBuilder = PluginManager.Instance.GetPluginBuilder(pluginPath, pluginFullName, pluginLanguage);
            Debug.Assert(this.pluginBuilder != null, String.Format("Can't load plugin with ID {0}: name: {1}", this.id, pluginPath.FullPath));

            this.packages = new MissionPluginPackageSet();
            this.packages.CaseSensitive = false;

            // we create a logging-Package automaticly here
            MissionPluginPackage loggingPackage = new MissionPluginPackage("Logging", Direction.Logging, "The default logging package.", false);
            loggingPackage.Columns.Add(new MissionPluginPackageColumn("SettingPackageName", typeof(string)));
            loggingPackage.Columns.Add(new MissionPluginPackageColumn("Percent", typeof(int)));
            loggingPackage.Columns.Add(new MissionPluginPackageColumn("Message", typeof(string)));
            this.packages.Tables.Add(loggingPackage);

            // create all plugin packages
            foreach (PackageDefinition packageDefinitionOn in this.pluginBuilder.PackageDefinitions)
            {
                MissionPluginPackage pluginPackage = new MissionPluginPackage(packageDefinitionOn);
                this.packages.Tables.Add(pluginPackage);
            }
        }

        public override string ToString()
        {
            return this.ClassName;
        }

        /// <summary>
        /// Returns the expected package of this plugin, searched by package-name. 
        /// </summary>
        /// <param name="packageName">Name of the package you search for.</param>
        /// <returns>Returns a package instance with the expected packageName.</returns>
        public MissionPluginPackage GetPackageByName(string packageName)
        {
            foreach (MissionPluginPackage packageOn in this.packages.Tables)
            {
                if (packageOn.TableName == packageName)
                {
                    return packageOn;
                }        
            }
            return null;
        }

        public List<MissionPluginPackage> GetPackages(Direction packageDirection)
        {
            List<MissionPluginPackage> packages = new List<MissionPluginPackage>();
            foreach (MissionPluginPackage packageOn in this.packages.Tables)
            {
                if (packageOn.Direction == (packageDirection & packageOn.Direction))
                {
                    packages.Add(packageOn);
                }
            }
            return packages;
        }

        /// <summary>
        /// Determines whether the package contains any error messages.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if there is at least one error message; otherwise, <c>false</c>.
        /// </returns>
        public bool HasErrorMessages()
        {
            return (this.LoggingPackage.Select("SettingPackageName = 'Error'").Length > 0);
        }

        /// <summary>
        /// Gets all error messages.
        /// </summary>
        /// <returns></returns>
        public List<string> GetErrorMessages()
        {
            List<string> errorMessages = new List<string>();            
            foreach (DataRow errorRowOn in this.LoggingPackage.Select("SettingPackageName = 'Error'"))
            {
                errorMessages.Add(errorRowOn["Message"].ToString());
            }
            return errorMessages;
        }

        /// <summary>
        /// Starts the plugin-Instance to work with given setting-parameters.
        /// </summary>
        internal void Start()
        {
            try
            {
                IPlugin pluginReference = (IPlugin)this.pluginBuilder.GetInstance();
                pluginReference.Initialize();
                pluginReference.Generate(ref this.packages);
                pluginReference.Dispose();
            }
            catch (Exception exception)
            {
                MissionPluginPackage loggingPackage = this.GetPackageByName("Logging");
                DataRow newLoggingRow = loggingPackage.NewRow();
                newLoggingRow["SettingPackageName"] = "Error";
                newLoggingRow["Percent"] = 0;
                newLoggingRow["Message"] = exception.Message;
                loggingPackage.Rows.Add(newLoggingRow);

                logger.Fatal(exception.Message);
            }
        }


        /// <summary>
        /// Sets all plugins to the default setting using the following rules:
        /// Delete the whole content from every logging and output package 
        /// Deletes the whole content or schema from all input linked packages.
        /// </summary>
        public void Reset()
        {
            // clear all logging and output-packages!
            foreach (MissionPluginPackage packageOn in this.GetPackages(Direction.Logging | Direction.Output))
            {
                packageOn.Clear();

                if (true == packageOn.IsSoftPackage)
                {
                    packageOn.Columns.Clear();
                }
            }
            
            // clear all packages if they are linked-packages
            foreach (MissionPluginPackage packageOn in this.GetPackages(Direction.All))
            {
                if (true == packageOn.HasReference || packageOn.GetColumnsWithColumnReference().Count > 0)
                {
                    packageOn.Clear();

                    if (true == packageOn.IsSoftPackage && false == packageOn.HasAutonomousSequence)
                    {
                        packageOn.Columns.Clear();
                    }
                }
            }

            // refresh all cell references
            foreach (MissionPluginPackage packageOn in this.GetPackages(Direction.Input))
            {
                foreach (MissionPluginPackageColumn columnOn in packageOn.GetColumnsWithCellReference())
                {
                    DataRow firstRow = packageOn.Rows[0];
                    firstRow[columnOn] = DBNull.Value;
                }
            }
        }

        public override void Add(MissionPluginNode pluginNode, RelativePluginPosition relativePluginPosition)
        {
            Console.WriteLine("Cannot add to a MissionPluginNode");
        }

        public override void Append(MissionPluginNode pluginNode)
        {
            Console.WriteLine("Cannot append to a MissionPluginNode");
        }

        public override void GetPlugins(ref List<MissionPluginProxy> plugins)
        {
            plugins.Add(this);
        }
    }
}
